const http = require('http');
const path = require('path');
const fs = require('fs');
const Koa = require('koa');
const app = new Koa();
const cors = require('koa-cors');
require('koa-validate')(app);
const json = require('koa-json');
const static = require('koa-static');
const onerror = require('koa-onerror');
const koabody = require('koa-body');
// const koajwt = require('koa-jwt');
const jwksRsa = require('jwks-rsa');
const bearerToken = require('koa-bearer-token');
const jwt = require('jsonwebtoken');
const bodyparser = require('koa-bodyparser');
const compress = require('koa-compress');
const log4js = require('koa-log4');
const responseTime = require('koa-response-time');
const co = require('co');
const convert = require('koa-convert');
const health = require('koa-ping');
const nconf = require('nconf');
const ms = require('ms');
const NodeCache = require("node-cache");
const template = require('art-template');
const render = require('koa-art-template');
const _ = require('lodash');
const session = require('koa-session');
const cookie = require('cookie');

const CONFIG = {
    key: 'koa:sess',
    /** (string) cookie key (default is koa:sess) */
    /** (number || 'session') maxAge in ms (default is 1 days) */
    /** 'session' will result in a cookie that expires when session/browser is closed */
    /** Warning: If a session cookie is stolen, this cookie will never expire */
    maxAge: 86400000,
    overwrite: true,
    /** (boolean) can overwrite or not (default true) */
    httpOnly: false,
    /** (boolean) httpOnly or not (default true) */
    signed: true,
    /** (boolean) signed or not (default true) */
    rolling: false,
    /** (boolean) Force a session identifier cookie to be set on every response. The expiration is reset to the original maxAge, resetting the expiration countdown. default is false **/
};

app.use(session(CONFIG, app));

const logger = log4js.getLogger("app");
// logger.level = 'debug';

// error handler
onerror(app);
logger.info("app is starting!");

global.prefixPath = path.resolve(__dirname, '');

const NODE_ENV = nconf.get('NODE_ENV');

const cache = new NodeCache({
    stdTTL: 100,
    checkperiod: 120
});
app.cache = cache;

app.use(static(path.join(__dirname, '../dist')));

// 模版公共参数
var render_runtime = {
    site_addr: ''
};

let filters = {
    watermark: (value) => {
        let url = value + nconf.get("qiniu").watermark;
        return url;
    },
    dateFormat: (UTCDateString) => {
        if (!UTCDateString) {
            return '-';
        }

        function formatFunc(str) { //格式化显示
            return str > 9 ? str : '0' + str
        }

        var date2 = new Date(UTCDateString); //这步是关键
        var year = date2.getFullYear();
        var mon = formatFunc(date2.getMonth() + 1);
        var day = formatFunc(date2.getDate());
        var hour = date2.getHours();
        // var noon = hour >= 12 ? 'PM' : 'AM';
        // hour = hour>=12?hour-12:hour;
        hour = formatFunc(hour);
        var min = formatFunc(date2.getMinutes());
        var sec = formatFunc(date2.getSeconds());
        var dateStr = year + '-' + mon + '-' + day + ' ' + ' ' + hour + ':' + min + ':' + sec;
        return dateStr;
    },
    subStr: (value) =>{
        return value.substring(0,8)+"..."
    }
};

const render_options = {

    // 是否开启对模板输出语句自动编码功能。为 false 则关闭编码输出功能
    // escape 可以防范 XSS 攻击
    escape: true,
    // 启动模板引擎调试模式。如果为 true: {cache:false, minimize:false, compileDebug:true}
    // 	debug: NODE_ENV !== 'production',
    debug: false,
    // bail 如果为 true，编译错误与运行时错误都会抛出异常
    bail: true,
    // 是否开启缓存
    cache: true,
    // 是否开启压缩。它会运行 htmlMinifier，将页面 HTML、CSS、CSS 进行压缩输出
    // 如果模板包含没有闭合的 HTML 标签，请不要打开 minimize，否则可能被 htmlMinifier 修复或过滤
    minimize: false, // TEST --> true
    // 是否编译调试版
    compileDebug: false,
    // HTML 压缩器配置。参见 https://github.com/kangax/html-minifier
    htmlMinifierOptions: {
        collapseWhitespace: false, // TEST --> true
        minifyCSS: false, // TEST --> true
        minifyJS: false, // TEST --> true
        // 运行时自动合并：rules.map(rule => rule.test)
        ignoreCustomFragments: []
    },
    // 错误事件。仅在 bail 为 false 时生效
    onerror: onerror,
    // 模板根目录。如果 filename 字段不是本地路径，则在 root 查找模板
    root: path.join(__dirname, '../views'),
    // 默认后缀名。如果没有后缀名，则会自动添加 extname
    extname: '.html',
    // 忽略的变量。被模板编译器忽略的模板变量列表
    ignore: [],
    // filters
    imports: filters,

};

render(app, render_options);

app.keys = ['3NGoOMT@6Iea*OPk*TZ1UnKeXpa#*LIu'];
app.secret = 'RkI5MjI5OUY5ODc1N0Q4QzM0OUYzNkVGMTJDOUEzQkFCOTU3NjE2Rg';

// middlewares
app.use(responseTime());

// http request log
app.use(log4js.koaLogger(log4js.getLogger("http"), {
    level: 'auto'
}));
// compress middleware for koa
app.use(compress({
    threshold: 2048,
    flush: require('zlib').Z_SYNC_FLUSH
}));
app.use(convert(cors()));
// pretty-printed JSON response middleware
app.use(json());
// bodyparse middleware for koa
app.use(koabody({
    multipart: true
}));
// app.use(bodyparser({
// 	onerror: function (err, ctx) {
// 		ctx.throw('body parse error', 422);
// 	}
// }));

app.use(convert(health('/sys/info')));


/**
 * 程序启动后读取所以的站点信息，并注入到app
 * @type {any}
 */
const site_service = require(global.prefixPath + '/services/site');
site_service.find({status: `online`}).then(rep => {
    app.sites = rep;
});


/**
 * 解析当前站点
 */
app.use(async (ctx, next) => {
    let subdomain = ctx.subdomains && ctx.subdomains[0];
    if (!subdomain) {
        let url = ctx.originalUrl;
        let strs = url.split("/");
        subdomain = strs[1];
    }

    let def_site = {};
    for (let i = 0; i < app.sites.length; i++) {
        let site = app.sites[i];
        if (site.subDomain === 'nj') {
            def_site = site
        }
        if (site.subDomain === subdomain) {
            ctx.site = site
        }
    }

    if (!ctx.site) {
        ctx.site = def_site;
    }

    // 将sites已经site都注入到ctx.state
    ctx.state.sites = ctx.app.sites;
    ctx.state.site = ctx.site;

    try {
        await next();
    } catch (err) {
        if (err) {
            err.status = err.statusCode || err.status || 500;
        }
        throw err;
    }
});

// ------------- jwt begin ---------------

app.use(convert(bearerToken({
    bodyKey: 'access_token',
    queryKey: 'access_token',
    headerKey: 'Bearer',
    reqKey: 'token'
})));


// const jwksHost = "https://sandrino.auth0.com";
// const audience = "urn:my-resource-server";
// const issuer = "https://my-authz-server/";
//
//
// const client = jwksRsa({
// 	strictSsl: true, // Default value
// 	jwksUri: 'https://sandrino.auth0.com/.well-known/jwks.json'
// });

// client.getSigningKey(kid, (err, key) => {
// 	const signingKey = key.publicKey || key.rsaPublicKey;
// 	console.log(signingKey);
// 	app.signingKey = signingKey;
//
// 	var token = jwt.sign({
// 		exp: Math.floor(Date.now() / 1000) + parseInt(nconf.get('jwt').expiretime),
// 		name: "shumin"
// 	}, app.secret, {algorithm: 'RS256', keyid: kid});
// 	console.log(token);
// 	app.token = token;
// });

// app.secret = jwksRsa.koaJwtSecret({
// 	cache: true,
// 	cacheMaxEntries: 5, // Default value
// 	cacheMaxAge: ms('10h'), // Default value
// 	rateLimit: true,
// 	jwksRequestsPerMinute: 2,
// 	jwksUri: `${jwksHost}/.well-known/jwks.json`
// });

// app.use(koajwt({
// 	secret: app.secret,
// }).unless({path: [/^\/public/, /^\/login/]}));

// 前置token验证
const user_service = require(global.prefixPath + '/services/user');
app.use(async (ctx, next) => {
    var path = ctx.path;
    // 静态资源等不用验证
    var ignorePaths = [/^\/public/, /^\/images/, /^\/stylesheets/, /^\/javascripts/];

    var ignore = ignorePaths.some(function (p) {
        return (typeof p === 'string' && p === path) ||
            (p instanceof RegExp && !!p.exec(path));
    });

    if (ignore) {
        return next();
    }

    var cookies = cookie.parse(ctx.req.headers.cookie || '');
    var cookieToken = cookies.access_token;

    // 优先解析cookie中的token
    var token = cookieToken ||
        (ctx.request.body ? ctx.request.body.access_token : null) ||
        (ctx.query ? ctx.query.access_token : null);

    console.log(token);

    if (!token) {
        return next();
    }

    var decodedObj = null;
    // 验证token
    await async function () {
        // logger.debug("verify the token!");
        return new Promise(function (resolve, reject) {
            jwt.verify(token, app.secret, function (err, decoded) {
                if (err) {
                    ctx.body = {
                        ok: 0,
                        msg: 'token verify fail'
                    };
                    reject();
                } else {
                    // add the decoded user name and org name to the request object
                    // for the downstream code to use
                    // logger.debug('Decoded from JWT token: decoded - ' + JSON.stringify(decoded));
                    decodedObj = decoded;
                    resolve();
                }
            });
        });
    }();

    if (decodedObj && decodedObj.name) {
        var userinfo = await user_service.findByName(decodedObj.name);
        ctx.state.userinfo = userinfo;
        ctx.state.userinfoStr = JSON.stringify(userinfo);
        ctx.decoded = decodedObj;
        console.log('userinfo: ', ctx.state.userinfo);
        ctx.hasLogin = true;
    } else {
        ctx.state.userinfo = {};
    }

    // logger.debug("invork next()!");
    return next();
});

// ------------- jwt end ---------------

// 自定义监听 Error 事件
app.on('error', (err, ctx) => {
    console.error('app error: ', err);
});

// ------------- routes start --------------------

// 扫描路由并注册
const util = require('./commons/util');
const routes_dir = path.resolve(__dirname, './routes');
const route_files = util.findFileSync(routes_dir);
_(route_files).forEach((file) => {
    // file = file.replace(__dirname, '.');
    let router = require(file);
    app.use(router.routes(), router.allowedMethods());
});

// ------------- routes end --------------------

logger.info("app start complete!");

exports = module.exports = app;
